#include "gameview.h"

#include "labelednumber.h"
#include "gameitem.h"
#include "plane.h"

#include <random>

#include <QLabel>
#include <QPainter>
#include <QTimer>
#include <QLCDNumber>
#include <QMovie>
#include <QMouseEvent>
#include <QApplication>
#include <QFile>

GameView::GameView(QWidget *parent) : QWidget(parent,Qt::Window) {
    setFixedSize(GAME_WIDTH, GAME_HEIGHT);
    map.load(":/background/img_bg_level_1.jpg");


    hp_number = new LabeledNumber("生命", this);
    hp_number->move(16, QWidget::height() - hp_number->height() - 16);
    hp_number->raise();

    oil_number = new LabeledNumber("燃油", this);
    oil_number->move(160, QWidget::height() - hp_number->height() - 16);
    score_number = new LabeledNumber("分数", this);
    score_number->move(300, QWidget::height() - hp_number->height() - 16);



    update_timer = new QTimer(this);
    update_timer->setInterval(0);
    connect(update_timer, &QTimer::timeout, this, &GameView::updateGame);
    hero = nullptr;
    init();// 初始化游戏

    bgm.setSource(QUrl("qrc:/music/bg.wav"));
    bgm.setLoopCount(QSoundEffect::Infinite);
}


GameView::~GameView() = default;

void GameView::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    painter.drawPixmap(0, map_pos, map);
    painter.drawPixmap(0,0, map, 0, map.height() - map_pos, 0, 0);

    for(auto && item: items){
        item->paint(&painter);
    }
}

void GameView::updateGame() {
    using namespace std::chrono;
    // 计算刷新间隔时间（毫秒）
    auto now = std::chrono::steady_clock::now();
    interval_ms = duration_cast<duration<qreal, std::milli>>(now - prev_update).count();
    map_pos += interval_ms * GAME_MAP_SPEED;
    if(map_pos >= GAME_HEIGHT){
        map_pos -= GAME_HEIGHT;
    }

    addEnemy(interval_ms);
    updateOil(interval_ms);

    for(auto && item: items){
        item->update(interval_ms);
    }
    if(!gameEnd){
       checkCollides(); // 检测碰撞
    }
    handleOutRangeItem(); // 处理越界

    prev_update = now;
    repaint();
    hp_number->update();
}
void GameView::start() {
    setFocus();
    if(!bgm.isPlaying()){
        bgm.play();
    }
    bgm.setVolume(0.5);
    prev_update = std::chrono::steady_clock::now();
    update_timer->start();
}


void GameView::pause() {
    update_timer->stop();
    bgm.setVolume(0.2);
    emit gamePause();
}


void GameView::end() {
    bgm.stop();
    update_timer->stop();
    init();
}


void GameView::reset() {
    hero = nullptr;
    init();
}

void GameView::init() {
    // 清空可能存在的旧的游戏单位
    update_timer->stop();
    for(GameItem *item: items){
        if(item != hero){
            item->deleteLater();
        }
    }
    items.clear();
    // 清空敌方列表
    enemies.clear();
    // 清空子弹
    enemies_bullets.clear();
    hero_bullets.clear();
    // 初始化玩家单位
    if(hero == nullptr){
        hero = new HeroPlane(PlaneFactor::getHeroPlane("hero2"));
        hero->setView(this);
        addItem(hero);
    }
    addItem(hero);
    hero->setPos({0.5 * QWidget::width() - hero->getBoundingSize().width()/2, 0.7 * QWidget::height()});
    forward = left = backward = right = false;

    oil = 100;
    oil_number->setNumber(oil);

    hp = 500;
    hp_number->setNumber(hp);

    score = 0;
    score_number->setNumber(score);

    gameEnd = false;
}

void GameView::addItem(GameItem *item) {
    if(items.contains(item)){
        return;
    }
    item->setParent(this);
    item->setView(this);
    items.append(item);
}
void GameView::removeItem(GameItem *item) {
    if(items.contains(item)){
        items.removeAll(item);
    }
}
void GameView::keyPressEvent(QKeyEvent *event) {
    switch (event->key()) {
        case Qt::Key_W:
            if(!forward && !gameEnd){
                forward = true;
                hero->setSpeed(hero->getSpeed() + QPointF{0.0, -hero->getMaxSpeed()});
            }
            break;
        case Qt::Key_A:
            if(!left && !gameEnd){
                left = true;
                hero->setSpeed(hero->getSpeed() + QPointF{-hero->getMaxSpeed(), 0.0});
            }
            break;
        case Qt::Key_S:
            if(!backward && !gameEnd){
                backward = true;
                hero->setSpeed(hero->getSpeed() + QPointF{0.0, hero->getMaxSpeed()});
            }
            break;
        case Qt::Key_D:
            if(!right && !gameEnd){
                right = true;
                hero->setSpeed(hero->getSpeed() + QPointF{hero->getMaxSpeed(), 0.0});
            }
            break;
        case Qt::Key_Escape:
            pause();
            break;
        case Qt::Key_1:
            hero->setBullet(BulletFactor::getHeroBullet("normal"));
            break;
        case Qt::Key_2:
            hero->setBullet(BulletFactor::getHeroBullet("heavy"));
            break;
        case Qt::Key_3:
            hero->setBullet(BulletFactor::getHeroBullet("fast"));
            break;
        case Qt::Key_4:
            hero->setBullet(BulletFactor::getHeroBullet("missile"));
            break;
        default:
            QWidget::keyPressEvent(event);
    }
}
void GameView::keyReleaseEvent(QKeyEvent *event) {
    switch (event->key()) {
        case Qt::Key_W:
            if(forward && !gameEnd){
                forward = false;
                hero->setSpeed(hero->getSpeed() - QPointF{0.0, -hero->getMaxSpeed()});
            }
            break;
        case Qt::Key_A:
            if(left && !gameEnd){
                left = false;
                hero->setSpeed(hero->getSpeed() - QPointF{-hero->getMaxSpeed(), 0.0});
            }
            break;
        case Qt::Key_S:
            if(backward && !gameEnd){
                backward = false;
                hero->setSpeed(hero->getSpeed() - QPointF{0.0, hero->getMaxSpeed()});
            }
            break;
        case Qt::Key_D:
            if(right && !gameEnd){
                right = false;
                hero->setSpeed(hero->getSpeed() - QPointF{hero->getMaxSpeed(), 0.0});
            }
            break;
        default:
            QWidget::keyReleaseEvent(event);
    }
}
void GameView::checkCollides() {
    Bullet *need_delete_bullet = nullptr;
    for(auto &&bullet : enemies_bullets){
        if(hero->collidesWithItem(bullet)){
            need_delete_bullet = bullet;
            hp -= bullet->damage();
            hp_number->setNumber(hp);
            if(hp <= 0){
                Bomb *bomb = new Bomb(this);
                bomb->setPos(hero->getPos());
                addItem(bomb);
                connect(bomb, &Bomb::finished,this, [this,bomb]{
                    removeItem(bomb);
                    bomb->deleteLater();
                });
                bomb->start();
                failed();
                break;
            }
        }
    }
    if(need_delete_bullet){
        removeItem(need_delete_bullet);
        enemies_bullets.removeOne(need_delete_bullet);
        need_delete_bullet->deleteLater();
    }
    QList<Bullet *> hit_bullets{};
    for(auto &&bullet: hero_bullets){
        QList<Plane *> hit_planes{};
        for(auto &&enemy: enemies){
            if(bullet->collidesWithItem(enemy)){
                enemy->damage(bullet->damage());
                hit_bullets.append(bullet);
                if(enemy->getHp() <= 0){
                    enemy->setSpeed({0,0});
                    enemy->setCount(0);
                    hit_planes.append(enemy);
                    Bomb *bomb = new Bomb(this);
                    bomb->setPos(enemy->getPos());
                    addItem(bomb);
                    connect(bomb, &Bomb::finished,this, [this,bomb, enemy]{
                        removeItem(bomb);
                        removeItem(enemy);
                        bomb->deleteLater();
                        enemy->deleteLater();
                    });
                    bomb->start();
                    score += 300;
                    score_number->setNumber(score);
                    break;
                }
            }
        }
        for(auto &enemy: hit_planes){
            enemies.removeOne(enemy);
        }
    }
    for(auto &&b: hit_bullets){
        hero_bullets.removeOne(b);
        removeItem(b);
        b->deleteLater();
    }
}

void GameView::handleOutRangeItem() {
    // 不允许飞机越过边界
    QPointF pos = hero->getPos();
    if(pos.x() < 0){
        hero->setPos({0, hero->getPos().y()});
    }else if(pos.x() > QWidget::width() - hero->getBoundingSize().width()){
        hero->setPos({QWidget::width() - hero->getBoundingSize().width(), hero->getPos().y()});
    }

    if(pos.y() < 0){
        hero->setPos({hero->getPos().x(), 0});
    }else if(pos.y() > QWidget::height() - hero->getBoundingSize().height()){
        hero->setPos({hero->getPos().x(), QWidget::height()- hero->getBoundingSize().height()});
    }

    // 敌机碰到边界时，反向移动
    for(auto &&enemy: enemies){
        pos = enemy->getPos();
        if(pos.x() < 0){
            enemy->setPos({1, enemy->getPos().y()});
            enemy->setSpeed({-enemy->getSpeed().x(), enemy->getSpeed().y()});
        }else if(pos.x() > QWidget::width() - enemy->getBoundingSize().width()){
            enemy->setPos({QWidget::width() - enemy->getBoundingSize().width(), enemy->getPos().y()});
            enemy->setSpeed({-enemy->getSpeed().x(), enemy->getSpeed().y()});
        }

        if(pos.y() < 0){
            enemy->setPos({enemy->getPos().x(), 1});
            enemy->setSpeed({enemy->getSpeed().x(), -enemy->getSpeed().y()});
        }else if(pos.y() > QWidget::height() - 300){
            enemy->setPos({enemy->getPos().x(), QWidget::height() - 300.0});
            enemy->setSpeed({enemy->getSpeed().x(), -enemy->getSpeed().y()});
        }
    }
    QList<Bullet*> need_remove_hero_bullet{};
    for(auto &&bullet: hero_bullets){
        if(bullet->getPos().y() < 0 ){
            need_remove_hero_bullet.append(bullet);
        }
    }
    for(auto &&bullet: need_remove_hero_bullet){
        removeItem(bullet);
        hero_bullets.removeOne(bullet);
        bullet->deleteLater();
    }
    QList<Bullet*> need_remove_enemies_bullet{};
    for(auto &&bullet: enemies_bullets){
        if(bullet->getPos().y() > QWidget::height()){
            need_remove_enemies_bullet.append(bullet);
        }
    }
    for(auto &&bullet: need_remove_hero_bullet){
        removeItem(bullet);
        enemies_bullets.removeOne(bullet);
        bullet->deleteLater();
    }
}

void GameView::addHeroBullet(Bullet *bullet) {
    bullet->setView(this);
    hero_bullets.append(bullet);
}

void GameView::addEnemyBullet(Bullet *bullet) {
    bullet->setView(this);
    enemies_bullets.append(bullet);
}
void GameView::addEnemy(qreal interval) {
    addEnemyIntervalCount += interval;
    if(addEnemyIntervalCount > 1000){
        addEnemyIntervalCount -= 1000;
        if(!level.enemies.empty()){// 当前关卡有剩余飞机，则添加敌机
            addEnemyToView(
                    new EnemyPlane(PlaneFactor::getEnemyPlane(level.enemies.first()))
                    );
            level.enemies.removeFirst();
        } else if(enemies.empty()){ // 场景中没有敌机
            if(!level.boos.empty()){ // 添加Boos
                addEnemyToView(
                        new EnemyPlane(PlaneFactor::getEnemyPlane(level.boos.first()))
                        );
                level.boos.removeFirst();
            } else if(!level.next.isEmpty()){ // 有下一关
                hero->setBulletCount(hero->getBulletCount() + 1);
                applyToSetLevel(GameLevel::levelList[level.next]);
            } else{
                success();
            }
        }
    }
}

void GameView::addEnemyToView(EnemyPlane *plane){
    static std::random_device rd;  //Will be used to obtain a seed for the random number engine
    static std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    static std::uniform_real_distribution<qreal> random_pos_x(10, 500);
    std::uniform_real_distribution<qreal> random_speed_y(-plane->getMaxSpeed(), plane->getMaxSpeed());

    qreal insert_x = random_pos_x(gen);
    plane->setPos({insert_x, 5});
    plane->setSpeed({random_speed_y(gen), plane->getMaxSpeed()});
    plane->setView(this);
    addItem(plane);
    enemies.append(plane);
}
void GameView::updateOil(qreal interval) {
    static qreal interval_count = 0;
    interval_count += interval;
    if(interval_count > 1000){
        interval_count -= 1000;
        oil--;
        oil_number->setNumber(oil);
        if(oil <= 0){
            failed();
        }
    }
}
void GameView::applyToSetLevel(const GameLevel& level) {
    update_timer->stop();
    emit wantChangeLevel(level);
}
void GameView::setLevel(const GameLevel& level) {
    map = level.map;
    this->level = level;
    int cur_score = score; // 保留当前分数
    init();
    setScore(cur_score);
}
void GameView::success() {
    if(gameEnd){return;}
    gameEnd = true;
    hero->setSpeed({0,0});
    bgm.stop();
    emit gameFinished(true, score);
}
void GameView::failed() {
    gameEnd = true;
    hero->setSpeed({0,0});
    bgm.stop();
    emit gameFinished(false, score);
}
int GameView::getScore() const {
    return score;
}
void GameView::setScore(int score) {
    GameView::score = score;
    score_number->setNumber(score);
}
QString GameView::currentLevelId() const {
    return level.id;
}
void GameView::saveGame() const {
    QFile saveFile(QApplication::applicationDirPath().append("/save.dat"));
    saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
    QDataStream save(&saveFile);
    save<<score<<level.id<<hero->getBulletCount()<<hero->getBulletInfo().name;
    saveFile.close();
}
void GameView::loadSave() {
    QFile saveFile(QApplication::applicationDirPath().append("/save.dat"));
    if(!saveFile.exists()){
        return;
    }
    saveFile.open(QIODevice::ReadOnly);
    QDataStream saveIn(&saveFile);
    int score, bullet_count;
    QString level, bulletName;
    saveIn>>score>>level>>bullet_count>>bulletName;
    saveFile.close();
    saveFile.remove();
    setScore(score);
    hero->setBulletCount(bullet_count);
    hero->setBullet(BulletFactor::getHeroBullet(bulletName));
    applyToSetLevel(GameLevel::levelList[level]);
}
